package com.suyeer.basic.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.suyeer.basic.bean.BaseHttpResContent;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.HttpsURLConnection;
import javax.servlet.http.HttpServletResponse;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;

import static com.suyeer.basic.util.ConstUtil.*;

/**
 * @author jun 2018/10/19
 */
public class HttpResUtil {

    private final static String BOUNDARY = "----MyFormBoundarySMFEtUYQG6r5B920";

    /**
     * 将数据返回到前端
     *
     * @param bHttpResContent BaseHttpResContent
     * @param <T>             T
     */
    public static <T> void writeResult(BaseHttpResContent<T> bHttpResContent) {
        writeResult(bHttpResContent, null);
    }

    public static <T> void writeResult(BaseHttpResContent<T> bHttpResContent, boolean ifAllowALLOrigin) {
        writeResult(bHttpResContent, ifAllowALLOrigin ? ALLOW_ALL_ORIGIN : null);
    }

    /**
     * 将数据返回到前端
     *
     * @param bHttpResContent BaseHttpResContent
     * @param allowOrigin     String
     * @param <T>             T
     */
    public static <T> void writeResult(BaseHttpResContent<T> bHttpResContent,
                                       String allowOrigin) {
        try {
            HttpServletResponse response = bHttpResContent.getResponse();
            if (StringUtils.isNotBlank(allowOrigin)) {
                response.setHeader(ALLOW_ORIGIN, allowOrigin);
            }
            response.setContentType(ConstUtil.CONTENT_TYPE_APPLICATION_JSON);
            response.setCharacterEncoding(ConstUtil.DEFAULT_ENCODE);
            String retStr = JSON.toJSONString(bHttpResContent);
            PrintWriter pw = response.getWriter();
            pw.write(retStr);
            pw.close();
            pw.flush();
        } catch (Exception e) {
            LogUtil.error("返回数据异常: {}", e.getMessage());
        }
    }

    /**
     * 只返回核心数据, 没有code和message
     *
     * @param hrc BaseHttpResContent
     */
    public static void writeOnlyResult(BaseHttpResContent hrc) {
        writeOnlyResult(hrc, null);
    }

    /**
     * 只返回核心数据, 没有code和message
     *
     * @param hrc              BaseHttpResContent
     * @param ifAllowALLOrigin boolean
     */
    public static void writeOnlyResult(BaseHttpResContent hrc, boolean ifAllowALLOrigin) {
        writeOnlyResult(hrc, ifAllowALLOrigin ? ALLOW_ALL_ORIGIN : null);
    }

    /**
     * 只返回核心数据, 没有code和message
     *
     * @param hrc         BaseHttpResContent
     * @param allowOrigin String
     */
    public static void writeOnlyResult(BaseHttpResContent hrc, String allowOrigin) {
        try {
            HttpServletResponse response = hrc.getResponse();
            if (StringUtils.isNotBlank(allowOrigin)) {
                response.setHeader(ALLOW_ORIGIN, allowOrigin);
            }
            response.setCharacterEncoding(ConstUtil.DEFAULT_ENCODE);
            String retStr = String.valueOf((Object) hrc.getResult());
            PrintWriter pw = response.getWriter();
            pw.write(retStr);
            pw.close();
            pw.flush();
        } catch (Exception e) {
            LogUtil.error("返回数据异常: {}", e.getMessage());
        }
    }

    private static RequestConfig getRequestConfig() {
        RequestConfig defaultRequestConfig = RequestConfig.custom()
                .setSocketTimeout(60000)
                .setConnectTimeout(60000)
                .setConnectionRequestTimeout(60000)
                .build();
        return defaultRequestConfig;
    }

    public static JSONObject sendHttpPostRequest(String url, JSONObject params) {
        return sendHttpPostRequest(url, params, null);
    }

    public static JSONObject sendHttpPostRequest(String url, JSONObject params, JSONObject header) {
        JSONObject retObj = null;
        CloseableHttpClient httpClient = null;
        try {
            httpClient = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost(url);
            httpPost.setHeader("Content-type", "application/x-www-form-urlencoded");
            httpPost.setHeader(USER_AGENT, DEFAULT_USER_AGENT);
            httpPost.setHeader("Referer", BasicUtil.getUrlOrigin(url));
            httpPost.setHeader("Origin", BasicUtil.getUrlOrigin(url));
            httpPost.setHeader("Host", BasicUtil.getUrlHost(url));
            String paramsStr = getParamStr(params);
            if (header != null) {
                for (String key : header.keySet()) {
                    httpPost.setHeader(key, header.getString(key));
                    if ("ContentType".equalsIgnoreCase(key) || "Content-Type".equalsIgnoreCase(key)) {
                        httpPost.setHeader("Content-type", header.getString(key));
                        if (header.getString(key).toLowerCase().contains("application/json")) {
                            paramsStr = JSON.toJSONString(params);
                        }
                    }
                }
            }
            httpPost.setConfig(getRequestConfig());
            StringEntity entity = new StringEntity(paramsStr, ConstUtil.DEFAULT_ENCODE);
            httpPost.setEntity(entity);
            retObj = sendRequest(httpClient, httpPost);
        } catch (Exception e) {
            LogUtil.error("POST请求失败,URL: {}, Message: {}", url, e.getMessage(), e);
        } finally {
            closeHttpClient(httpClient);
        }
        return retObj;
    }

    private static String getParamStr(JSONObject params) {
        StringBuilder sb = new StringBuilder();
        if (params != null) {
            for (String k : params.keySet()) {
                sb.append(String.format("%s=%s&", k, params.getString(k)));
            }
        }
        return sb.toString();
    }

    public static String sendHttpPostRequest(String url, String xmlString) {
        String retStr = null;
        CloseableHttpClient httpClient = null;
        try {
            httpClient = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost(url);
            httpPost.setConfig(getRequestConfig());
            StringEntity entity = new StringEntity(StringUtils.isBlank(xmlString) ? "" : xmlString, ConstUtil.DEFAULT_ENCODE);
            httpPost.setEntity(entity);
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            if (httpEntity != null) {
                retStr = EntityUtils.toString(httpEntity, ConstUtil.DEFAULT_ENCODE);
            }
        } catch (Exception e) {
            LogUtil.error("POST请求失败,URL: {}, Message: {}", url, e.getMessage());
        } finally {
            closeHttpClient(httpClient);
        }
        return retStr;
    }

    public static JSONObject sendHttpGetRequest(String url) {
        JSONObject retObj = null;
        CloseableHttpClient httpClient = null;
        try {
            httpClient = HttpClients.createDefault();
            HttpGet httpGet = new HttpGet(url);
            httpGet.setConfig(getRequestConfig());
            retObj = sendRequest(httpClient, httpGet);
        } catch (Exception e) {
            LogUtil.error("GET请求失败,URL: {}, Message: {}", url, e.getMessage());
        } finally {
            closeHttpClient(httpClient);
        }
        return retObj;
    }

    private static JSONObject sendRequest(CloseableHttpClient chc, HttpUriRequest hur) throws Exception {
        CloseableHttpResponse response = chc.execute(hur);
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != HttpStatus.SC_OK) {
            throw new Exception("请求返回状态异常,状态码: " + statusCode);
        }
        return JSONObject.parseObject(EntityUtils.toString(response.getEntity(), ConstUtil.DEFAULT_ENCODE));
    }

    private static void closeHttpClient(CloseableHttpClient chc) {
        if (chc != null) {
            try {
                chc.close();
            } catch (IOException e) {
                LogUtil.error("httpClient对象关闭异常: {}", e.getMessage());
            }
        }
    }

    public static JSONObject sendSSLPostRequest(String url, JSONObject params) {
        return sendSSLRequest(url, getParamStr(params), ConstUtil.POST);
    }

    public static JSONObject sendSSLPostRequest(String url, String jsonString) {
        return sendSSLRequest(url, jsonString, ConstUtil.POST);
    }

    public static JSONObject sendSSLGetRequest(String url) {
        return sendSSLRequest(url, null, ConstUtil.GET);
    }

    private static JSONObject sendSSLRequest(String url, String params, String methodType) {
        JSONObject retJson = null;
        try {
            HttpsURLConnection httpsURLConnection = (HttpsURLConnection) (new URL(url).openConnection());
            httpsURLConnection.setConnectTimeout(60000);
            httpsURLConnection.setReadTimeout(60000);
            httpsURLConnection.setDoOutput(true);
            httpsURLConnection.setDoInput(true);
            httpsURLConnection.setUseCaches(false);
            httpsURLConnection.setRequestMethod(methodType);
            httpsURLConnection.setHostnameVerifier(BasicUtil.createHostnameVerifier(true));
            httpsURLConnection.setSSLSocketFactory(BasicUtil.createSSLSocketFactory("SSL", "SunJSSE"));
            if (methodType.equals(ConstUtil.GET)) {
                httpsURLConnection.connect();
            }
            if (StringUtils.isNotBlank(params)) {
                OutputStream outputStream = httpsURLConnection.getOutputStream();
                outputStream.write(params.getBytes(ConstUtil.DEFAULT_ENCODE));
                outputStream.close();
            }
            String retStr = IOUtils.toString(httpsURLConnection.getInputStream(), ConstUtil.DEFAULT_ENCODE);
            retJson = JSONObject.parseObject(retStr);
            httpsURLConnection.disconnect();
        } catch (Exception e) {
            LogUtil.error("发送请求异常: {}", e.getMessage());
        }
        return retJson;
    }

    /**
     * 发送包含文件的post请求(未完成)
     *
     * @param urlStr String
     * @param params JSONObject
     * @return JSONObject
     * @throws Exception Exception
     */
    @Deprecated
    public static JSONObject sendHttpFilePostRequest(String urlStr, JSONObject params) throws Exception {
        HttpURLConnection hc = null;
        String retStr = null;
        try {
            URL url = new URL(urlStr);
            hc = (HttpURLConnection) url.openConnection();
            hc.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
            hc.setRequestProperty("Charsert", ConstUtil.DEFAULT_ENCODE);
            hc.setDoOutput(true);
            hc.setDoInput(true);
            hc.setUseCaches(false);
            hc.setRequestMethod("POST");

            OutputStream dout = hc.getOutputStream();
            //1.先写文字形式的post流
            String boundary = BOUNDARY;
            StringBuffer resSB = new StringBuffer("\r\n");
            String endBoundary = "\r\n--" + boundary + "--\r\n";
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue().toString();
                if (StringUtils.isNotBlank(key)) {
                    resSB.append(String.format("Content-Disposition: form-data; name=%s\r\n\r\n%s\r\n--%s\r\n", key, value, boundary));
                }
            }
            String boundaryMessage = resSB.toString();
            dout.write(("--" + boundary + boundaryMessage).getBytes(ConstUtil.DEFAULT_ENCODE));
            //2.再写文件开式的post流
            resSB = new StringBuffer();
            resSB.append("Content-Disposition: form-data; name=Filedata; filename=class_img.jpg\r\nContent-Type: image/jpeg\r\n\r\n");
            dout.write(resSB.toString().getBytes(ConstUtil.DEFAULT_ENCODE));
            URL fileUrl = new URL(params.getString(ConstUtil.FILE));
            DataInputStream in = new DataInputStream(fileUrl.openConnection().getInputStream());
            dout.write(IOUtils.toByteArray(in));
            in.close();
            //3.最后写结尾
            dout.write(endBoundary.getBytes(ConstUtil.DEFAULT_ENCODE));
            dout.close();
            retStr = IOUtils.toString(hc.getInputStream(), ConstUtil.DEFAULT_ENCODE);
        } catch (Exception e) {
            LogUtil.error("发送请求失败: {}", e.getMessage());
        } finally {
            try {
                hc.disconnect();
            } catch (Exception e) {
                LogUtil.error("对象关闭异常: {}", e.getMessage());
            }
        }
        return JSONObject.parseObject(retStr);
    }
}
